# /*
#  *  Copyright 2021 籽籮籮 籵籮籮籯类籲籷籰
#  *
#  *  This includes splay Copyright (C) 1998-2021 Free Software Foundation, Inc.
#  *  This includes Copyright (C) 2008, 2011 Matthias Stallmann, Saurabh Gupta.
#  *  This includes Copyright (C) 2009 MiptVis GNU GPL version 3+ Boris Shurygin
#  *  Junio H Hamano gitster@pobox.com
#  *
#  *  This program is free software: you can redistribute it and/or modify
#  *  it under the terms of the GNU General Public License as published by
#  *  the Free Software Foundation, either version 3 of the License, or
#  *  (at your option) any later version.
#  *
#  *  This program is distributed in the hope that it will be useful,
#  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  *  GNU General Public License for more details.
#  *
#  *  You should have received a copy of the GNU General Public License
#  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
#  *
#  *  These are the four essential freedoms with GNU GPL software:
#  *  1: freedom to run the program, for any purpose
#  *  2: freedom to study how the program works, and change it to make it do what you wish
#  *  3: freedom to redistribute copies to help your Free Software friends
#  *  4: freedom to distribute copies of your modified versions to your Free Software friends
#  *   ,           ,
#  *  /             \
#  * ((__-^^-,-^^-__))
#  * `-_---'  `---_-'
#  *  `--|o`   'o|--'
#  *      \  `  /
#  *       ): :(
#  *       :o_o:
#  *        "-"
#  *
#  * SPDX-License-Identifier: GPL-3.0+
#  * License-Filename: LICENSE
#  */
# gml graph language lexer+parser to use with packcc parser generator
# this does not implement the special gml chars but that is never used
# this does only read using FILE stream but can be updated for gz stream
# this needs update to read from specified stream
# this needs mode updates to improve it more
# this parser has the extra benefit it does allow nodes specified after edges
# this parser does not implement those never used iso chars
# THE GENERATED PARSER WITH THIS SPECIAL VERSION OF PACKCC IS GNU GPL VERSION 3+
# Then the whole program is GNU GPL VERSION 3+
%prefix "gml"

%source{

/* some setting in packcc code */
#define BUFFER_INIT_SIZE 256*2
#define ARRAY_INIT_SIZE 2*2

#include <stdio.h>
#include <string.h>

#include "splay-tree.h"
#include "main.h"
#include "dpmem.h"
#include "hier.h"

/* internal gml data */
struct gmlnd
{
    char *id;	/* uniq node id number as string */
    long idlong;	/* uniq node id number */
    char *label;	/* optional node label */
};

struct gmled
{
    int num;	/* uniq edge number */
    char *source;	/* source node */
    int idsource;	/* source id number */
    char *target;	/* target node */
    int idtarget;	/* target id number */
    char *label;	/* optional label */
};

/* stream to parse */
static FILE *gmlstream = NULL;

/* debug on/off */
static int showdebug = 0;

/* error status */
static int status = 0;

/* parsed node data */
static splay_tree gmln = (splay_tree)0;

/* parsed edge data */
static splay_tree gmle = (splay_tree)0;

/* edge counter */
static int ecount = 0;

/* last parsed number */
static char *lastnum = NULL;

/* last parsed number */
static char *lastfpnum = NULL;

/* last id string */
static char *lastid = NULL;

/* last pair id string */
static char *lastpairid = NULL;

/* last string */
static char *laststring = NULL;

/* type of data parsed */
static int gtype = 0;

/* type of pair data */
static int pairmode = 0;

/* current node id */
static char *cur_node_id = NULL;

/* current node label */
static char *cur_node_label = NULL;

/* current edge data */
static char *cur_edge_source = NULL;
static char *cur_edge_target = NULL;
static char *cur_edge_label = NULL;

/* getchar() is same as getc(stdin) pcc_auxil_t auxil
 * #define PCC_GETCHAR(auxil) getchar()
 */
#define PCC_GETCHAR(auxil) getc(stdin)
#define PCC_BUFFERSIZE 1024*128

#define PCC_ERROR(auxil) \
    do { fprintf(stdout, "PCC_ERROR(auxil) says Syntax error\n"); status = 1; } while (0)

static const char *dbg_str[] = { "Evaluating rule", "Matched rule", "Abandoning rule" };

#define PCC_DEBUG(event, rule, level, pos, buffer, length) \
    if (showdebug) fprintf(stdout, "%*s%s %s @%d [%.*s]\n", (int)(level * 2), "", dbg_str[event], rule, (int)pos, (int)length,  buffer); fflush(stdout)

/* forward declarations */

/* init parser */
static void init00 (void);

/* switch between node[], edge[] etc. */
static void switcher (void);

/* add complete node/edge data */
static void adder (void);

/* handle a pair */
static void dopair (void);

/* copy data from parser to layouter */
static void copyin (void);

/* customized strdup() or use uniqstr() */
static char *gmlstrdup (const char *s);

}

# start of input
file <- _ head _ 'graph' _ list _ !.

# there can be optional version data before first graph statement which can be ignored.
head <- (pair)*

# the actual gml graph data
list <- '[' _ some_items* _ ']' _

# example node [ ... ] or edge [ ... ] or otherwise
some_items <-	id { switcher(); } list2
	/ pair { printf("pairmode=%d\n",pairmode); dopair(); pairmode=0; }

#
list2 <- '[' _ some_items* _ ']' _ { adder(); gtype = 0; }

# example: foo bar
pair <-	  (id { lastpairid = gmlstrdup(lastid); } string)	{ pairmode=1; printf ("'%s' '%s' (id string)\n",lastid,laststring); }
	/ (id { lastpairid = gmlstrdup(lastid); } id)	{ pairmode=2; printf ("'%s' '%s' (id id)\n",lastpairid,lastid); }
	/ (id { lastpairid = gmlstrdup(lastid); } fpnum)	{ pairmode=3; printf ("'%s' '%s' (id fpnum)\n",lastid,lastfpnum); }
	/ (id { lastpairid = gmlstrdup(lastid); } digit)	{ pairmode=4; printf ("'%s' '%s' (id digit)\n",lastid,lastnum); }

# (+/-) int number
digit <- < ( ('-' / '+')? [0-9]+ ) > _ { lastnum = gmlstrdup ($1); }

# (+/-) floating point number
fpnum <-  < ( ('-' / '+')? [0-9]* '.' [0-9]+ (['e''E'] ('-' / '+')? [0-9]+ )? ) > _ { lastfpnum = gmlstrdup($1); }

# gml identifier
id	<- < [a-zA-Z_]+[a-zA-Z_0-9]* > _	 { lastid = gmlstrdup($1); }

# string between "" with optional extra control chars
string	<- '"' < char* > '"' _ {  laststring = gmlstrdup($1);  }

# chars inside a string
char	<-
	'\\' "\""
	/ '\\' '\\'
	/ '\\' 'b'
	/ '\\' 'f'
	/ '\\' 'n'
	/ '\\' 'r'
	/ '\\' 't'
	/ (!"\"" .)

_	<-		(space / comment)*
space	<-	(' ' / '\t' / endofline)
comment	<-	'#' (!endofline .)*
endofline	<-	( '\r\n' / '\n' / '\r' / '\n\r' )		{ }

%%

/* parse the gml graph data */
int gmlmain(FILE *filestream, int gmldebug) {
    gml_context_t *ctx = (gml_context_t *)0;
    if (!filestream) {
	/* shouldnothappen return error status */
	return (1);
    }
    /* input parameters */
    showdebug = gmldebug;
    gmlstream = filestream;
    /* update here to read from stream instead of stdin */
    /* set no error status */
    status = 0;
    /* init parser */
    init00();
    ctx = gml_create(NULL);
    /* parse the data */
    for (;;)
    {
	fflush (stdout);
	fflush (stderr);
	if (status) {
	    break; /* stop at first parse error */
	}
	if (gml_parse(ctx, NULL) == 0) {
		break;
	}
    }
    gml_destroy(ctx);
    if (status == 0) {
	/* parsed ok, now check the data. */
	copyin ();
    }
    return (status);
}

/* init parser */
static void init00 (void)
{

    /* data for parsed nodes. key is the string of the uniq id number, value is struct with data */
    gmln = splay_tree_new(splay_tree_compare_strings, (splay_tree_delete_key_fn)0, (splay_tree_delete_value_fn)0);

    /* data for parsed edges. */
    gmle = splay_tree_new(splay_tree_compare_ints, (splay_tree_delete_key_fn)0, (splay_tree_delete_value_fn)0);

    /* edge counter */
    ecount = 0;

    /* type of data parsed */
    gtype = 0;

    /* type of pair data */
    pairmode = 0;

    return;
}

/* switch between node[], edge[] etc. */
static void switcher (void)
{
    if (!lastid) {
	/* shouldnothappen */
	/* type of data parsed */
	gtype = 0;
	return;
    }
    /* select what kind of data is being parsed */
    if (strcmp (lastid, "node")==0) {
	gtype = 1;
	printf("node\n");
    } else if (strcmp (lastid, "edge") == 0) {
	gtype = 2;
	printf("edge\n");
    } else {
	/* something else */
	if (gtype == 1) {
	    printf("inside node\n");
	} else if (gtype == 2) {
	    printf("inside edge\n");
	} else {
	    gtype = 0;
	    printf ("something else\n");
	}
    }
    return;
}

/* handle a parsed pair */
static void dopair (void)
{
	if (status) {
	    /* there has been a parse error */
	    return;
	}
	if (gtype == 1) {
		/* inside a node */
		if (strcmp (lastpairid, "id") == 0) {
			if (pairmode == 4) {
				/* example: node [ id 3 ] */
printf("node with id %s found\n",lastnum);
				if (cur_node_id) {
					printf ("node with multiple id numbers\n");
					/* add here routine to stop the parser */
					status = 1;
				} else {
					cur_node_id = gmlstrdup(lastnum);
				}
			} else {
				printf ("node id with bad format\n");
				/* add here routine to stop the parser */
				status = 1;
			}
		} else if (strcmp (lastpairid, "label") == 0) {
			if (pairmode == 1) {
				if (cur_node_label) {
					printf ("node with multiple labels\n");
					/* add here routine to stop the parser */
					status = 1;
				} else {
					cur_node_label = gmlstrdup(laststring);
				}
			} else {
				printf ("node label with bad format\n");
				/* add here routine to stop the parser */
				status = 1;
			}
		} else {
			/* other node details can be added here */
		}
	} else if (gtype == 2) {
		/* inside a edge */
		/* more checks to be added here */
		if (strcmp (lastpairid, "source") == 0) {
			if (pairmode == 4) {
				cur_edge_source = gmlstrdup(lastnum);
			}
		} else if (strcmp (lastpairid, "target") == 0) {
			if (pairmode == 4) {
				cur_edge_target = gmlstrdup(lastnum);
			}
		} else if (strcmp (lastpairid, "label") == 0) {
			if (pairmode == 1) {
				cur_edge_label = gmlstrdup(laststring);
			}
		} else {
			/* some other edge detail can be added here */
		}
	} else {

	}
	return;
}

/* add complete node/edge data */
static void adder (void)
{
    struct gmlnd *ndp = NULL;
    struct gmled *edp = NULL;
    if (status) {
	/* there has been a parse error */
	return;
    }
    printf("adder gtype=%d\n",gtype);
    if (gtype == 1) {
	/* node data */
	printf("add node id=%s label=%s\n", cur_node_id, cur_node_label);
	/* clear the current node data */
	ndp = dp_calloc (1,sizeof(struct gmlnd));
	ndp->id = gmlstrdup (cur_node_id);
	if (cur_node_label) {
		/* optional label */
		ndp->label = gmlstrdup (cur_node_label);
	}
	splay_tree_insert (gmln, (splay_tree_key) cur_node_id, (splay_tree_value) ndp);
	cur_node_id = NULL;
	cur_node_label = NULL;
    } else if (gtype == 2) {
	/* edge data */
	printf("add edge\n");
	printf("add egde source=%s target=%s label=%s\n", cur_edge_source, cur_edge_target, cur_edge_label);
	if (cur_edge_label) {
		/* optional label */
	}
	edp = dp_calloc (1,sizeof(struct gmled));
	edp->num = ecount++;
	edp->source = gmlstrdup(cur_edge_source);
	edp->target = gmlstrdup(cur_edge_target);
	if (cur_edge_label) {
	    edp->label = gmlstrdup(cur_edge_label);
	}
	splay_tree_insert (gmle, (splay_tree_key) ecount, (splay_tree_value) edp);
	cur_edge_source = NULL;
	cur_edge_target = NULL;
	cur_edge_label = NULL;
    } else {
	/* skip */
    }
    return;
}

/* copy data from parser to layouter, at error set status to 1 */
static void copyin (void)
{
    splay_tree_node nspn;
    splay_tree_node sspn;
    splay_tree_key key;
    struct gmlnd *ndp = NULL;
    struct gmled *edp = NULL;
    long n = 0;

    printf("%s():\n",__func__);

    /* first check the node data */
    nspn = splay_tree_min (gmln);

    while (nspn) {
	key = nspn->key;
	ndp = (struct gmlnd *)nspn->value;
	printf ("resulting parsed node %s with label %s\n", (char *)nspn->key, (char *)ndp->label);
	/* here the node id number is known to be correct but can be +/- 123 */
	/* to improve: use strtoll and check for signed 32 bit overflow */
	n = strtol ((char *)nspn->key, NULL, 10);
	if (n < 0) {
		printf ("node with negative number %s is a error\n", (char *)nspn->key);
		status = 1;
	}
	/* save the converted number */
	ndp->idlong = n; /* to improve: this can overflow. */
	nspn = splay_tree_successor (gmln, key);
    }

    /* stop at error */
    if (status) {
	return;
    }

    /* scan the optional edges and all node numbers must have been defined */
    nspn = splay_tree_min (gmle);

    while (nspn) {
	key = nspn->key;
	edp = (struct gmled *) nspn->value;
	printf ("edge from %s to %s label %s\n",edp->source,edp->target, edp->label);
	sspn = splay_tree_lookup (gmln, (splay_tree_key) edp->source);
	printf("%p for %s\n",(void *)sspn, edp->source);
	if (!sspn) {
	    printf ("node %s in edge %s to %s not defined\n", edp->source, edp->source, edp->target);
	    status = 1;
	    break;
	}
	sspn = splay_tree_lookup (gmln, (splay_tree_key) edp->target);
	printf("%p for %s\n",(void *)sspn, edp->source);
	if (!sspn) {
	    printf ("node %s in edge %s to %s not defined\n", edp->target, edp->source, edp->target);
	    status = 1;
	    break;
	}
	/* translate the char * numbers into int */
	n = strtol ((char *)edp->source, NULL, 10);
	/* improve this */
	edp->idsource = (int)n;
	n = strtol ((char *)edp->target, NULL, 10);
	/* improve this */
	edp->idtarget = (int)n;
	nspn = splay_tree_successor (gmle, key);
    }

    /* stop at error */
    if (status) {
	return;
    }

    /* node and edges are checked, copy the data into the layouter
    * nodes and edges have uniq numbers starting at 1 in nodenum and edgenum
    * in hier.c:
    * add_new_node(struct gml_graph *g, struct gml_graph *ro, int nr, int foundid, char *nodename, char *nodelabel, int ncolor, int nbcolor, struct gml_rl *rlabel, struct gml_hl *hlabel, int fontcolor, int ishtml);
    * add_new_edge(struct gml_graph *g, struct gml_graph *ro, int foundsource, int foundtarget, char *elabel, int ecolor, int style, char *fcompass, char *tcompass, int constraint, int ishtml);
    */

    /* create the graph data rootgraph */
    maingraph = dp_calloc (1 ,sizeof(struct gml_graph));

    /* copy the node data */
    nspn = splay_tree_min (gmln);

    while (nspn) {
	key = nspn->key;
	ndp = (struct gmlnd *)nspn->value;
	/* node numbers start at 1 */
	maingraph->nodenum++;
	/* for gml tester no colors or dot specifics are needed */
	add_new_node(maingraph, NULL,maingraph->nodenum,(int)ndp->idlong,ndp->id,ndp->label,0 /* ncolor */,0 /* nbcolor */, NULL, NULL, 0, 0);
	nspn = splay_tree_successor (gmln, key);
    }

    /* copy the edge data */
    nspn = splay_tree_min (gmle);

    while (nspn) {
	key = nspn->key;
	edp = (struct gmled *) nspn->value;
	/* for gml tester no colors are needed */
	/* colors and dot specifics are skipped here. */
	add_new_edge(maingraph, NULL, edp->idsource, edp->idtarget, edp->label, 0, 0, NULL, NULL, 0, 0);
	nspn = splay_tree_successor (gmle, key);
    }

    return;
}

/* customized strdup() or use uniqstr() */
static char *gmlstrdup (const char *s)
{
    char *ret = NULL;
    char *s2 = NULL;
    size_t len = 0;
    s2 = (char *)s;
    if (s == NULL) {
	/* shouldnothappen */
	s2 = " ";
    }
    if (strlen (s) == 0) {
	/* shouldnothappen */
	s2 = " ";
    }
    len = strlen (s2);
    ret = dp_calloc (1, (len + 1) );
    memmove (ret, s2, len);
    return (ret);
}

/* end. */
